// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/compiler/js-create-lowering.h"

#include "src/code-factory.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/allocation-builder-inl.h"
#include "src/compiler/common-operator.h"
#include "src/compiler/compilation-dependencies.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/js-operator.h"
#include "src/compiler/linkage.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/node.h"
#include "src/compiler/operator-properties.h"
#include "src/compiler/simplified-operator.h"
#include "src/compiler/state-values-utils.h"
#include "src/objects-inl.h"
#include "src/objects/arguments.h"
#include "src/objects/hash-table-inl.h"
#include "src/objects/heap-number.h"
#include "src/objects/js-collection-iterator.h"
#include "src/objects/js-generator.h"
#include "src/objects/js-promise.h"
#include "src/objects/js-regexp-inl.h"

namespace v8 {
namespace internal {
    namespace compiler {

        namespace {

            // Retrieves the frame state holding actual argument values.
            Node* GetArgumentsFrameState(Node* frame_state)
            {
                Node* const outer_state = NodeProperties::GetFrameStateInput(frame_state);
                FrameStateInfo outer_state_info = FrameStateInfoOf(outer_state->op());
                return outer_state_info.type() == FrameStateType::kArgumentsAdaptor
                    ? outer_state
                    : frame_state;
            }

            // When initializing arrays, we'll unfold the loop if the number of
            // elements is known to be of this type.
            const int kElementLoopUnrollLimit = 16;

            // Limits up to which context allocations are inlined.
            const int kFunctionContextAllocationLimit = 16;
            const int kBlockContextAllocationLimit = 16;

        } // namespace

        Reduction JSCreateLowering::Reduce(Node* node)
        {
            DisallowHeapAccess disallow_heap_access;
            switch (node->opcode()) {
            case IrOpcode::kJSCreate:
                return ReduceJSCreate(node);
            case IrOpcode::kJSCreateArguments:
                return ReduceJSCreateArguments(node);
            case IrOpcode::kJSCreateArray:
                return ReduceJSCreateArray(node);
            case IrOpcode::kJSCreateArrayIterator:
                return ReduceJSCreateArrayIterator(node);
            case IrOpcode::kJSCreateAsyncFunctionObject:
                return ReduceJSCreateAsyncFunctionObject(node);
            case IrOpcode::kJSCreateBoundFunction:
                return ReduceJSCreateBoundFunction(node);
            case IrOpcode::kJSCreateClosure:
                return ReduceJSCreateClosure(node);
            case IrOpcode::kJSCreateCollectionIterator:
                return ReduceJSCreateCollectionIterator(node);
            case IrOpcode::kJSCreateIterResultObject:
                return ReduceJSCreateIterResultObject(node);
            case IrOpcode::kJSCreateStringIterator:
                return ReduceJSCreateStringIterator(node);
            case IrOpcode::kJSCreateKeyValueArray:
                return ReduceJSCreateKeyValueArray(node);
            case IrOpcode::kJSCreatePromise:
                return ReduceJSCreatePromise(node);
            case IrOpcode::kJSCreateLiteralArray:
            case IrOpcode::kJSCreateLiteralObject:
                return ReduceJSCreateLiteralArrayOrObject(node);
            case IrOpcode::kJSCreateLiteralRegExp:
                return ReduceJSCreateLiteralRegExp(node);
            case IrOpcode::kJSCreateEmptyLiteralArray:
                return ReduceJSCreateEmptyLiteralArray(node);
            case IrOpcode::kJSCreateEmptyLiteralObject:
                return ReduceJSCreateEmptyLiteralObject(node);
            case IrOpcode::kJSCreateFunctionContext:
                return ReduceJSCreateFunctionContext(node);
            case IrOpcode::kJSCreateWithContext:
                return ReduceJSCreateWithContext(node);
            case IrOpcode::kJSCreateCatchContext:
                return ReduceJSCreateCatchContext(node);
            case IrOpcode::kJSCreateBlockContext:
                return ReduceJSCreateBlockContext(node);
            case IrOpcode::kJSCreateGeneratorObject:
                return ReduceJSCreateGeneratorObject(node);
            case IrOpcode::kJSCreateObject:
                return ReduceJSCreateObject(node);
            default:
                break;
            }
            return NoChange();
        }

        Reduction JSCreateLowering::ReduceJSCreate(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCreate, node->opcode());
            Node* const new_target = NodeProperties::GetValueInput(node, 1);
            Node* const effect = NodeProperties::GetEffectInput(node);
            Node* const control = NodeProperties::GetControlInput(node);

            base::Optional<MapRef> initial_map = NodeProperties::GetJSCreateMap(broker(), node);
            if (!initial_map.has_value())
                return NoChange();

            JSFunctionRef original_constructor = HeapObjectMatcher(new_target).Ref(broker()).AsJSFunction();
            SlackTrackingPrediction slack_tracking_prediction = dependencies()->DependOnInitialMapInstanceSizePrediction(
                original_constructor);

            // Emit code to allocate the JSObject instance for the
            // {original_constructor}.
            AllocationBuilder a(jsgraph(), effect, control);
            a.Allocate(slack_tracking_prediction.instance_size());
            a.Store(AccessBuilder::ForMap(), *initial_map);
            a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(),
                jsgraph()->EmptyFixedArrayConstant());
            a.Store(AccessBuilder::ForJSObjectElements(),
                jsgraph()->EmptyFixedArrayConstant());
            for (int i = 0; i < slack_tracking_prediction.inobject_property_count();
                 ++i) {
                a.Store(AccessBuilder::ForJSObjectInObjectProperty(*initial_map, i),
                    jsgraph()->UndefinedConstant());
            }

            RelaxControls(node);
            a.FinishAndChange(node);
            return Changed(node);
        }

        Reduction JSCreateLowering::ReduceJSCreateArguments(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCreateArguments, node->opcode());
            CreateArgumentsType type = CreateArgumentsTypeOf(node->op());
            Node* const frame_state = NodeProperties::GetFrameStateInput(node);
            Node* const outer_state = frame_state->InputAt(kFrameStateOuterStateInput);
            Node* const control = graph()->start();
            FrameStateInfo state_info = FrameStateInfoOf(frame_state->op());
            SharedFunctionInfoRef shared(broker(),
                state_info.shared_info().ToHandleChecked());

            // Use the ArgumentsAccessStub for materializing both mapped and unmapped
            // arguments object, but only for non-inlined (i.e. outermost) frames.
            if (outer_state->opcode() != IrOpcode::kFrameState) {
                switch (type) {
                case CreateArgumentsType::kMappedArguments: {
                    // TODO(mstarzinger): Duplicate parameters are not handled yet.
                    if (shared.has_duplicate_parameters())
                        return NoChange();
                    Node* const callee = NodeProperties::GetValueInput(node, 0);
                    Node* const context = NodeProperties::GetContextInput(node);
                    Node* effect = NodeProperties::GetEffectInput(node);
                    Node* const arguments_frame = graph()->NewNode(simplified()->ArgumentsFrame());
                    Node* const arguments_length = graph()->NewNode(
                        simplified()->ArgumentsLength(
                            shared.internal_formal_parameter_count(), false),
                        arguments_frame);
                    // Allocate the elements backing store.
                    bool has_aliased_arguments = false;
                    Node* const elements = effect = AllocateAliasedArguments(
                        effect, control, context, arguments_frame, arguments_length, shared,
                        &has_aliased_arguments);
                    // Load the arguments object map.
                    Node* const arguments_map = jsgraph()->Constant(
                        has_aliased_arguments
                            ? native_context().fast_aliased_arguments_map()
                            : native_context().sloppy_arguments_map());
                    // Actually allocate and initialize the arguments object.
                    AllocationBuilder a(jsgraph(), effect, control);
                    Node* properties = jsgraph()->EmptyFixedArrayConstant();
                    STATIC_ASSERT(JSSloppyArgumentsObject::kSize == 5 * kTaggedSize);
                    a.Allocate(JSSloppyArgumentsObject::kSize);
                    a.Store(AccessBuilder::ForMap(), arguments_map);
                    a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);
                    a.Store(AccessBuilder::ForJSObjectElements(), elements);
                    a.Store(AccessBuilder::ForArgumentsLength(), arguments_length);
                    a.Store(AccessBuilder::ForArgumentsCallee(), callee);
                    RelaxControls(node);
                    a.FinishAndChange(node);
                    return Changed(node);
                }
                case CreateArgumentsType::kUnmappedArguments: {
                    Node* effect = NodeProperties::GetEffectInput(node);
                    Node* const arguments_frame = graph()->NewNode(simplified()->ArgumentsFrame());
                    Node* const arguments_length = graph()->NewNode(
                        simplified()->ArgumentsLength(
                            shared.internal_formal_parameter_count(), false),
                        arguments_frame);
                    // Allocate the elements backing store.
                    Node* const elements = effect = graph()->NewNode(simplified()->NewArgumentsElements(0),
                        arguments_frame, arguments_length, effect);
                    // Load the arguments object map.
                    Node* const arguments_map = jsgraph()->Constant(native_context().strict_arguments_map());
                    // Actually allocate and initialize the arguments object.
                    AllocationBuilder a(jsgraph(), effect, control);
                    Node* properties = jsgraph()->EmptyFixedArrayConstant();
                    STATIC_ASSERT(JSStrictArgumentsObject::kSize == 4 * kTaggedSize);
                    a.Allocate(JSStrictArgumentsObject::kSize);
                    a.Store(AccessBuilder::ForMap(), arguments_map);
                    a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);
                    a.Store(AccessBuilder::ForJSObjectElements(), elements);
                    a.Store(AccessBuilder::ForArgumentsLength(), arguments_length);
                    RelaxControls(node);
                    a.FinishAndChange(node);
                    return Changed(node);
                }
                case CreateArgumentsType::kRestParameter: {
                    Node* effect = NodeProperties::GetEffectInput(node);
                    Node* const arguments_frame = graph()->NewNode(simplified()->ArgumentsFrame());
                    Node* const rest_length = graph()->NewNode(
                        simplified()->ArgumentsLength(
                            shared.internal_formal_parameter_count(), true),
                        arguments_frame);
                    // Allocate the elements backing store. Since NewArgumentsElements
                    // copies from the end of the arguments adapter frame, this is a suffix
                    // of the actual arguments.
                    Node* const elements = effect = graph()->NewNode(simplified()->NewArgumentsElements(0),
                        arguments_frame, rest_length, effect);
                    // Load the JSArray object map.
                    Node* const jsarray_map = jsgraph()->Constant(
                        native_context().js_array_packed_elements_map());
                    // Actually allocate and initialize the jsarray.
                    AllocationBuilder a(jsgraph(), effect, control);
                    Node* properties = jsgraph()->EmptyFixedArrayConstant();
                    STATIC_ASSERT(JSArray::kSize == 4 * kTaggedSize);
                    a.Allocate(JSArray::kSize);
                    a.Store(AccessBuilder::ForMap(), jsarray_map);
                    a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);
                    a.Store(AccessBuilder::ForJSObjectElements(), elements);
                    a.Store(AccessBuilder::ForJSArrayLength(PACKED_ELEMENTS), rest_length);
                    RelaxControls(node);
                    a.FinishAndChange(node);
                    return Changed(node);
                }
                }
                UNREACHABLE();
            } else if (outer_state->opcode() == IrOpcode::kFrameState) {
                // Use inline allocation for all mapped arguments objects within inlined
                // (i.e. non-outermost) frames, independent of the object size.
                if (type == CreateArgumentsType::kMappedArguments) {
                    Node* const callee = NodeProperties::GetValueInput(node, 0);
                    Node* const context = NodeProperties::GetContextInput(node);
                    Node* effect = NodeProperties::GetEffectInput(node);
                    // TODO(mstarzinger): Duplicate parameters are not handled yet.
                    if (shared.has_duplicate_parameters())
                        return NoChange();
                    // Choose the correct frame state and frame state info depending on
                    // whether there conceptually is an arguments adaptor frame in the call
                    // chain.
                    Node* const args_state = GetArgumentsFrameState(frame_state);
                    if (args_state->InputAt(kFrameStateParametersInput)->opcode() == IrOpcode::kDeadValue) {
                        // This protects against an incompletely propagated DeadValue node.
                        // If the FrameState has a DeadValue input, then this node will be
                        // pruned anyway.
                        return NoChange();
                    }
                    FrameStateInfo args_state_info = FrameStateInfoOf(args_state->op());
                    // Prepare element backing store to be used by arguments object.
                    bool has_aliased_arguments = false;
                    Node* const elements = AllocateAliasedArguments(
                        effect, control, args_state, context, shared, &has_aliased_arguments);
                    effect = elements->op()->EffectOutputCount() > 0 ? elements : effect;
                    // Load the arguments object map.
                    Node* const arguments_map = jsgraph()->Constant(
                        has_aliased_arguments ? native_context().fast_aliased_arguments_map()
                                              : native_context().sloppy_arguments_map());
                    // Actually allocate and initialize the arguments object.
                    AllocationBuilder a(jsgraph(), effect, control);
                    Node* properties = jsgraph()->EmptyFixedArrayConstant();
                    int length = args_state_info.parameter_count() - 1; // Minus receiver.
                    STATIC_ASSERT(JSSloppyArgumentsObject::kSize == 5 * kTaggedSize);
                    a.Allocate(JSSloppyArgumentsObject::kSize);
                    a.Store(AccessBuilder::ForMap(), arguments_map);
                    a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);
                    a.Store(AccessBuilder::ForJSObjectElements(), elements);
                    a.Store(AccessBuilder::ForArgumentsLength(), jsgraph()->Constant(length));
                    a.Store(AccessBuilder::ForArgumentsCallee(), callee);
                    RelaxControls(node);
                    a.FinishAndChange(node);
                    return Changed(node);
                } else if (type == CreateArgumentsType::kUnmappedArguments) {
                    // Use inline allocation for all unmapped arguments objects within inlined
                    // (i.e. non-outermost) frames, independent of the object size.
                    Node* effect = NodeProperties::GetEffectInput(node);
                    // Choose the correct frame state and frame state info depending on
                    // whether there conceptually is an arguments adaptor frame in the call
                    // chain.
                    Node* const args_state = GetArgumentsFrameState(frame_state);
                    if (args_state->InputAt(kFrameStateParametersInput)->opcode() == IrOpcode::kDeadValue) {
                        // This protects against an incompletely propagated DeadValue node.
                        // If the FrameState has a DeadValue input, then this node will be
                        // pruned anyway.
                        return NoChange();
                    }
                    FrameStateInfo args_state_info = FrameStateInfoOf(args_state->op());
                    // Prepare element backing store to be used by arguments object.
                    Node* const elements = AllocateArguments(effect, control, args_state);
                    effect = elements->op()->EffectOutputCount() > 0 ? elements : effect;
                    // Load the arguments object map.
                    Node* const arguments_map = jsgraph()->Constant(native_context().strict_arguments_map());
                    // Actually allocate and initialize the arguments object.
                    AllocationBuilder a(jsgraph(), effect, control);
                    Node* properties = jsgraph()->EmptyFixedArrayConstant();
                    int length = args_state_info.parameter_count() - 1; // Minus receiver.
                    STATIC_ASSERT(JSStrictArgumentsObject::kSize == 4 * kTaggedSize);
                    a.Allocate(JSStrictArgumentsObject::kSize);
                    a.Store(AccessBuilder::ForMap(), arguments_map);
                    a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);
                    a.Store(AccessBuilder::ForJSObjectElements(), elements);
                    a.Store(AccessBuilder::ForArgumentsLength(), jsgraph()->Constant(length));
                    RelaxControls(node);
                    a.FinishAndChange(node);
                    return Changed(node);
                } else if (type == CreateArgumentsType::kRestParameter) {
                    int start_index = shared.internal_formal_parameter_count();
                    // Use inline allocation for all unmapped arguments objects within inlined
                    // (i.e. non-outermost) frames, independent of the object size.
                    Node* effect = NodeProperties::GetEffectInput(node);
                    // Choose the correct frame state and frame state info depending on
                    // whether there conceptually is an arguments adaptor frame in the call
                    // chain.
                    Node* const args_state = GetArgumentsFrameState(frame_state);
                    if (args_state->InputAt(kFrameStateParametersInput)->opcode() == IrOpcode::kDeadValue) {
                        // This protects against an incompletely propagated DeadValue node.
                        // If the FrameState has a DeadValue input, then this node will be
                        // pruned anyway.
                        return NoChange();
                    }
                    FrameStateInfo args_state_info = FrameStateInfoOf(args_state->op());
                    // Prepare element backing store to be used by the rest array.
                    Node* const elements = AllocateRestArguments(effect, control, args_state, start_index);
                    effect = elements->op()->EffectOutputCount() > 0 ? elements : effect;
                    // Load the JSArray object map.
                    Node* const jsarray_map = jsgraph()->Constant(native_context().js_array_packed_elements_map());
                    // Actually allocate and initialize the jsarray.
                    AllocationBuilder a(jsgraph(), effect, control);
                    Node* properties = jsgraph()->EmptyFixedArrayConstant();

                    // -1 to minus receiver
                    int argument_count = args_state_info.parameter_count() - 1;
                    int length = std::max(0, argument_count - start_index);
                    STATIC_ASSERT(JSArray::kSize == 4 * kTaggedSize);
                    a.Allocate(JSArray::kSize);
                    a.Store(AccessBuilder::ForMap(), jsarray_map);
                    a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);
                    a.Store(AccessBuilder::ForJSObjectElements(), elements);
                    a.Store(AccessBuilder::ForJSArrayLength(PACKED_ELEMENTS),
                        jsgraph()->Constant(length));
                    RelaxControls(node);
                    a.FinishAndChange(node);
                    return Changed(node);
                }
            }

            return NoChange();
        }

        Reduction JSCreateLowering::ReduceJSCreateGeneratorObject(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCreateGeneratorObject, node->opcode());
            Node* const closure = NodeProperties::GetValueInput(node, 0);
            Node* const receiver = NodeProperties::GetValueInput(node, 1);
            Node* const context = NodeProperties::GetContextInput(node);
            Type const closure_type = NodeProperties::GetType(closure);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* const control = NodeProperties::GetControlInput(node);
            if (closure_type.IsHeapConstant()) {
                DCHECK(closure_type.AsHeapConstant()->Ref().IsJSFunction());
                JSFunctionRef js_function = closure_type.AsHeapConstant()->Ref().AsJSFunction();
                if (!js_function.has_initial_map())
                    return NoChange();

                SlackTrackingPrediction slack_tracking_prediction = dependencies()->DependOnInitialMapInstanceSizePrediction(js_function);

                MapRef initial_map = js_function.initial_map();
                DCHECK(initial_map.instance_type() == JS_GENERATOR_OBJECT_TYPE || initial_map.instance_type() == JS_ASYNC_GENERATOR_OBJECT_TYPE);

                // Allocate a register file.
                SharedFunctionInfoRef shared = js_function.shared();
                DCHECK(shared.HasBytecodeArray());
                int parameter_count_no_receiver = shared.internal_formal_parameter_count();
                int size = parameter_count_no_receiver + shared.GetBytecodeArray().register_count();
                AllocationBuilder ab(jsgraph(), effect, control);
                ab.AllocateArray(size, factory()->fixed_array_map());
                for (int i = 0; i < size; ++i) {
                    ab.Store(AccessBuilder::ForFixedArraySlot(i),
                        jsgraph()->UndefinedConstant());
                }
                Node* parameters_and_registers = effect = ab.Finish();

                // Emit code to allocate the JS[Async]GeneratorObject instance.
                AllocationBuilder a(jsgraph(), effect, control);
                a.Allocate(slack_tracking_prediction.instance_size());
                Node* empty_fixed_array = jsgraph()->EmptyFixedArrayConstant();
                Node* undefined = jsgraph()->UndefinedConstant();
                a.Store(AccessBuilder::ForMap(), initial_map);
                a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), empty_fixed_array);
                a.Store(AccessBuilder::ForJSObjectElements(), empty_fixed_array);
                a.Store(AccessBuilder::ForJSGeneratorObjectContext(), context);
                a.Store(AccessBuilder::ForJSGeneratorObjectFunction(), closure);
                a.Store(AccessBuilder::ForJSGeneratorObjectReceiver(), receiver);
                a.Store(AccessBuilder::ForJSGeneratorObjectInputOrDebugPos(), undefined);
                a.Store(AccessBuilder::ForJSGeneratorObjectResumeMode(),
                    jsgraph()->Constant(JSGeneratorObject::kNext));
                a.Store(AccessBuilder::ForJSGeneratorObjectContinuation(),
                    jsgraph()->Constant(JSGeneratorObject::kGeneratorExecuting));
                a.Store(AccessBuilder::ForJSGeneratorObjectParametersAndRegisters(),
                    parameters_and_registers);

                if (initial_map.instance_type() == JS_ASYNC_GENERATOR_OBJECT_TYPE) {
                    a.Store(AccessBuilder::ForJSAsyncGeneratorObjectQueue(), undefined);
                    a.Store(AccessBuilder::ForJSAsyncGeneratorObjectIsAwaiting(),
                        jsgraph()->ZeroConstant());
                }

                // Handle in-object properties, too.
                for (int i = 0; i < slack_tracking_prediction.inobject_property_count();
                     ++i) {
                    a.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i),
                        undefined);
                }
                a.FinishAndChange(node);
                return Changed(node);
            }
            return NoChange();
        }

        // Constructs an array with a variable {length} when no upper bound
        // is known for the capacity.
        Reduction JSCreateLowering::ReduceNewArray(
            Node* node, Node* length, MapRef initial_map, ElementsKind elements_kind,
            AllocationType allocation,
            const SlackTrackingPrediction& slack_tracking_prediction)
        {
            DCHECK_EQ(IrOpcode::kJSCreateArray, node->opcode());
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Constructing an Array via new Array(N) where N is an unsigned
            // integer, always creates a holey backing store.
            ASSIGN_RETURN_NO_CHANGE_IF_DATA_MISSING(
                initial_map,
                initial_map.AsElementsKind(GetHoleyElementsKind(elements_kind)));

            // Check that the {limit} is an unsigned integer in the valid range.
            // This has to be kept in sync with src/runtime/runtime-array.cc,
            // where this limit is protected.
            length = effect = graph()->NewNode(
                simplified()->CheckBounds(VectorSlotPair()), length,
                jsgraph()->Constant(JSArray::kInitialMaxFastElementArray), effect,
                control);

            // Construct elements and properties for the resulting JSArray.
            Node* elements = effect = graph()->NewNode(IsDoubleElementsKind(initial_map.elements_kind())
                    ? simplified()->NewDoubleElements(allocation)
                    : simplified()->NewSmiOrObjectElements(allocation),
                length, effect, control);
            Node* properties = jsgraph()->EmptyFixedArrayConstant();

            // Perform the allocation of the actual JSArray object.
            AllocationBuilder a(jsgraph(), effect, control);
            a.Allocate(slack_tracking_prediction.instance_size(), allocation);
            a.Store(AccessBuilder::ForMap(), initial_map);
            a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);
            a.Store(AccessBuilder::ForJSObjectElements(), elements);
            a.Store(AccessBuilder::ForJSArrayLength(initial_map.elements_kind()), length);
            for (int i = 0; i < slack_tracking_prediction.inobject_property_count();
                 ++i) {
                a.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i),
                    jsgraph()->UndefinedConstant());
            }
            RelaxControls(node);
            a.FinishAndChange(node);
            return Changed(node);
        }

        // Constructs an array with a variable {length} when an actual
        // upper bound is known for the {capacity}.
        Reduction JSCreateLowering::ReduceNewArray(
            Node* node, Node* length, int capacity, MapRef initial_map,
            ElementsKind elements_kind, AllocationType allocation,
            const SlackTrackingPrediction& slack_tracking_prediction)
        {
            DCHECK(node->opcode() == IrOpcode::kJSCreateArray || node->opcode() == IrOpcode::kJSCreateEmptyLiteralArray);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Determine the appropriate elements kind.
            if (NodeProperties::GetType(length).Max() > 0.0) {
                elements_kind = GetHoleyElementsKind(elements_kind);
            }
            ASSIGN_RETURN_NO_CHANGE_IF_DATA_MISSING(
                initial_map, initial_map.AsElementsKind(elements_kind));
            DCHECK(IsFastElementsKind(elements_kind));

            // Setup elements and properties.
            Node* elements;
            if (capacity == 0) {
                elements = jsgraph()->EmptyFixedArrayConstant();
            } else {
                elements = effect = AllocateElements(effect, control, elements_kind, capacity, allocation);
            }
            Node* properties = jsgraph()->EmptyFixedArrayConstant();

            // Perform the allocation of the actual JSArray object.
            AllocationBuilder a(jsgraph(), effect, control);
            a.Allocate(slack_tracking_prediction.instance_size(), allocation);
            a.Store(AccessBuilder::ForMap(), initial_map);
            a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);
            a.Store(AccessBuilder::ForJSObjectElements(), elements);
            a.Store(AccessBuilder::ForJSArrayLength(elements_kind), length);
            for (int i = 0; i < slack_tracking_prediction.inobject_property_count();
                 ++i) {
                a.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i),
                    jsgraph()->UndefinedConstant());
            }
            RelaxControls(node);
            a.FinishAndChange(node);
            return Changed(node);
        }

        Reduction JSCreateLowering::ReduceNewArray(
            Node* node, std::vector<Node*> values, MapRef initial_map,
            ElementsKind elements_kind, AllocationType allocation,
            const SlackTrackingPrediction& slack_tracking_prediction)
        {
            DCHECK_EQ(IrOpcode::kJSCreateArray, node->opcode());
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Determine the appropriate elements kind.
            DCHECK(IsFastElementsKind(elements_kind));
            ASSIGN_RETURN_NO_CHANGE_IF_DATA_MISSING(
                initial_map, initial_map.AsElementsKind(elements_kind));

            // Check {values} based on the {elements_kind}. These checks are guarded
            // by the {elements_kind} feedback on the {site}, so it's safe to just
            // deoptimize in this case.
            if (IsSmiElementsKind(elements_kind)) {
                for (auto& value : values) {
                    if (!NodeProperties::GetType(value).Is(Type::SignedSmall())) {
                        value = effect = graph()->NewNode(
                            simplified()->CheckSmi(VectorSlotPair()), value, effect, control);
                    }
                }
            } else if (IsDoubleElementsKind(elements_kind)) {
                for (auto& value : values) {
                    if (!NodeProperties::GetType(value).Is(Type::Number())) {
                        value = effect = graph()->NewNode(simplified()->CheckNumber(VectorSlotPair()), value,
                            effect, control);
                    }
                    // Make sure we do not store signaling NaNs into double arrays.
                    value = graph()->NewNode(simplified()->NumberSilenceNaN(), value);
                }
            }

            // Setup elements, properties and length.
            Node* elements = effect = AllocateElements(effect, control, elements_kind, values, allocation);
            Node* properties = jsgraph()->EmptyFixedArrayConstant();
            Node* length = jsgraph()->Constant(static_cast<int>(values.size()));

            // Perform the allocation of the actual JSArray object.
            AllocationBuilder a(jsgraph(), effect, control);
            a.Allocate(slack_tracking_prediction.instance_size(), allocation);
            a.Store(AccessBuilder::ForMap(), initial_map);
            a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);
            a.Store(AccessBuilder::ForJSObjectElements(), elements);
            a.Store(AccessBuilder::ForJSArrayLength(elements_kind), length);
            for (int i = 0; i < slack_tracking_prediction.inobject_property_count();
                 ++i) {
                a.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i),
                    jsgraph()->UndefinedConstant());
            }
            RelaxControls(node);
            a.FinishAndChange(node);
            return Changed(node);
        }

        Reduction JSCreateLowering::ReduceJSCreateArray(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCreateArray, node->opcode());
            CreateArrayParameters const& p = CreateArrayParametersOf(node->op());
            int const arity = static_cast<int>(p.arity());
            base::Optional<AllocationSiteRef> site_ref;
            {
                Handle<AllocationSite> site;
                if (p.site().ToHandle(&site)) {
                    site_ref = AllocationSiteRef(broker(), site);
                }
            }
            AllocationType allocation = AllocationType::kYoung;

            base::Optional<MapRef> initial_map = NodeProperties::GetJSCreateMap(broker(), node);
            if (!initial_map.has_value())
                return NoChange();

            Node* new_target = NodeProperties::GetValueInput(node, 1);
            JSFunctionRef original_constructor = HeapObjectMatcher(new_target).Ref(broker()).AsJSFunction();
            SlackTrackingPrediction slack_tracking_prediction = dependencies()->DependOnInitialMapInstanceSizePrediction(
                original_constructor);

            // Tells whether we are protected by either the {site} or a
            // protector cell to do certain speculative optimizations.
            bool can_inline_call = false;

            // Check if we have a feedback {site} on the {node}.
            ElementsKind elements_kind = initial_map->elements_kind();
            if (site_ref) {
                elements_kind = site_ref->GetElementsKind();
                can_inline_call = site_ref->CanInlineCall();
                allocation = dependencies()->DependOnPretenureMode(*site_ref);
                dependencies()->DependOnElementsKind(*site_ref);
            } else {
                CellRef array_constructor_protector(
                    broker(), factory()->array_constructor_protector());
                can_inline_call = array_constructor_protector.value().AsSmi() == Isolate::kProtectorValid;
            }

            if (arity == 0) {
                Node* length = jsgraph()->ZeroConstant();
                int capacity = JSArray::kPreallocatedArrayElements;
                return ReduceNewArray(node, length, capacity, *initial_map, elements_kind,
                    allocation, slack_tracking_prediction);
            } else if (arity == 1) {
                Node* length = NodeProperties::GetValueInput(node, 2);
                Type length_type = NodeProperties::GetType(length);
                if (!length_type.Maybe(Type::Number())) {
                    // Handle the single argument case, where we know that the value
                    // cannot be a valid Array length.
                    elements_kind = GetMoreGeneralElementsKind(
                        elements_kind, IsHoleyElementsKind(elements_kind) ? HOLEY_ELEMENTS : PACKED_ELEMENTS);
                    return ReduceNewArray(node, std::vector<Node*> { length }, *initial_map,
                        elements_kind, allocation,
                        slack_tracking_prediction);
                }
                if (length_type.Is(Type::SignedSmall()) && length_type.Min() >= 0 && length_type.Max() <= kElementLoopUnrollLimit && length_type.Min() == length_type.Max()) {
                    int capacity = static_cast<int>(length_type.Max());
                    return ReduceNewArray(node, length, capacity, *initial_map, elements_kind,
                        allocation, slack_tracking_prediction);
                }
                if (length_type.Maybe(Type::UnsignedSmall()) && can_inline_call) {
                    return ReduceNewArray(node, length, *initial_map, elements_kind,
                        allocation, slack_tracking_prediction);
                }
            } else if (arity <= JSArray::kInitialMaxFastElementArray) {
                // Gather the values to store into the newly created array.
                bool values_all_smis = true, values_all_numbers = true,
                     values_any_nonnumber = false;
                std::vector<Node*> values;
                values.reserve(p.arity());
                for (int i = 0; i < arity; ++i) {
                    Node* value = NodeProperties::GetValueInput(node, 2 + i);
                    Type value_type = NodeProperties::GetType(value);
                    if (!value_type.Is(Type::SignedSmall())) {
                        values_all_smis = false;
                    }
                    if (!value_type.Is(Type::Number())) {
                        values_all_numbers = false;
                    }
                    if (!value_type.Maybe(Type::Number())) {
                        values_any_nonnumber = true;
                    }
                    values.push_back(value);
                }

                // Try to figure out the ideal elements kind statically.
                if (values_all_smis) {
                    // Smis can be stored with any elements kind.
                } else if (values_all_numbers) {
                    elements_kind = GetMoreGeneralElementsKind(
                        elements_kind, IsHoleyElementsKind(elements_kind) ? HOLEY_DOUBLE_ELEMENTS : PACKED_DOUBLE_ELEMENTS);
                } else if (values_any_nonnumber) {
                    elements_kind = GetMoreGeneralElementsKind(
                        elements_kind, IsHoleyElementsKind(elements_kind) ? HOLEY_ELEMENTS : PACKED_ELEMENTS);
                } else if (!can_inline_call) {
                    // We have some crazy combination of types for the {values} where
                    // there's no clear decision on the elements kind statically. And
                    // we don't have a protection against deoptimization loops for the
                    // checks that are introduced in the call to ReduceNewArray, so
                    // we cannot inline this invocation of the Array constructor here.
                    return NoChange();
                }
                return ReduceNewArray(node, values, *initial_map, elements_kind, allocation,
                    slack_tracking_prediction);
            }
            return NoChange();
        }

        Reduction JSCreateLowering::ReduceJSCreateArrayIterator(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCreateArrayIterator, node->opcode());
            CreateArrayIteratorParameters const& p = CreateArrayIteratorParametersOf(node->op());
            Node* iterated_object = NodeProperties::GetValueInput(node, 0);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Create the JSArrayIterator result.
            AllocationBuilder a(jsgraph(), effect, control);
            a.Allocate(JSArrayIterator::kSize, AllocationType::kYoung,
                Type::OtherObject());
            a.Store(AccessBuilder::ForMap(),
                native_context().initial_array_iterator_map());
            a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(),
                jsgraph()->EmptyFixedArrayConstant());
            a.Store(AccessBuilder::ForJSObjectElements(),
                jsgraph()->EmptyFixedArrayConstant());
            a.Store(AccessBuilder::ForJSArrayIteratorIteratedObject(), iterated_object);
            a.Store(AccessBuilder::ForJSArrayIteratorNextIndex(),
                jsgraph()->ZeroConstant());
            a.Store(AccessBuilder::ForJSArrayIteratorKind(),
                jsgraph()->Constant(static_cast<int>(p.kind())));
            RelaxControls(node);
            a.FinishAndChange(node);
            return Changed(node);
        }

        Reduction JSCreateLowering::ReduceJSCreateAsyncFunctionObject(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCreateAsyncFunctionObject, node->opcode());
            int const register_count = RegisterCountOf(node->op());
            Node* closure = NodeProperties::GetValueInput(node, 0);
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Node* promise = NodeProperties::GetValueInput(node, 2);
            Node* context = NodeProperties::GetContextInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Create the register file.
            AllocationBuilder ab(jsgraph(), effect, control);
            ab.AllocateArray(register_count, factory()->fixed_array_map());
            for (int i = 0; i < register_count; ++i) {
                ab.Store(AccessBuilder::ForFixedArraySlot(i),
                    jsgraph()->UndefinedConstant());
            }
            Node* parameters_and_registers = effect = ab.Finish();

            // Create the JSAsyncFunctionObject result.
            AllocationBuilder a(jsgraph(), effect, control);
            a.Allocate(JSAsyncFunctionObject::kSize);
            Node* empty_fixed_array = jsgraph()->EmptyFixedArrayConstant();
            a.Store(AccessBuilder::ForMap(),
                native_context().async_function_object_map());
            a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), empty_fixed_array);
            a.Store(AccessBuilder::ForJSObjectElements(), empty_fixed_array);
            a.Store(AccessBuilder::ForJSGeneratorObjectContext(), context);
            a.Store(AccessBuilder::ForJSGeneratorObjectFunction(), closure);
            a.Store(AccessBuilder::ForJSGeneratorObjectReceiver(), receiver);
            a.Store(AccessBuilder::ForJSGeneratorObjectInputOrDebugPos(),
                jsgraph()->UndefinedConstant());
            a.Store(AccessBuilder::ForJSGeneratorObjectResumeMode(),
                jsgraph()->Constant(JSGeneratorObject::kNext));
            a.Store(AccessBuilder::ForJSGeneratorObjectContinuation(),
                jsgraph()->Constant(JSGeneratorObject::kGeneratorExecuting));
            a.Store(AccessBuilder::ForJSGeneratorObjectParametersAndRegisters(),
                parameters_and_registers);
            a.Store(AccessBuilder::ForJSAsyncFunctionObjectPromise(), promise);
            a.FinishAndChange(node);
            return Changed(node);
        }

        namespace {

            MapRef MapForCollectionIterationKind(const NativeContextRef& native_context,
                CollectionKind collection_kind,
                IterationKind iteration_kind)
            {
                switch (collection_kind) {
                case CollectionKind::kSet:
                    switch (iteration_kind) {
                    case IterationKind::kKeys:
                        UNREACHABLE();
                    case IterationKind::kValues:
                        return native_context.set_value_iterator_map();
                    case IterationKind::kEntries:
                        return native_context.set_key_value_iterator_map();
                    }
                    break;
                case CollectionKind::kMap:
                    switch (iteration_kind) {
                    case IterationKind::kKeys:
                        return native_context.map_key_iterator_map();
                    case IterationKind::kValues:
                        return native_context.map_value_iterator_map();
                    case IterationKind::kEntries:
                        return native_context.map_key_value_iterator_map();
                    }
                    break;
                }
                UNREACHABLE();
            }

        } // namespace

        Reduction JSCreateLowering::ReduceJSCreateCollectionIterator(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCreateCollectionIterator, node->opcode());
            CreateCollectionIteratorParameters const& p = CreateCollectionIteratorParametersOf(node->op());
            Node* iterated_object = NodeProperties::GetValueInput(node, 0);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Load the OrderedHashTable from the {receiver}.
            Node* table = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForJSCollectionTable()),
                iterated_object, effect, control);

            // Create the JSArrayIterator result.
            AllocationBuilder a(jsgraph(), effect, control);
            a.Allocate(JSCollectionIterator::kSize, AllocationType::kYoung,
                Type::OtherObject());
            a.Store(AccessBuilder::ForMap(),
                MapForCollectionIterationKind(native_context(), p.collection_kind(),
                    p.iteration_kind()));
            a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(),
                jsgraph()->EmptyFixedArrayConstant());
            a.Store(AccessBuilder::ForJSObjectElements(),
                jsgraph()->EmptyFixedArrayConstant());
            a.Store(AccessBuilder::ForJSCollectionIteratorTable(), table);
            a.Store(AccessBuilder::ForJSCollectionIteratorIndex(),
                jsgraph()->ZeroConstant());
            RelaxControls(node);
            a.FinishAndChange(node);
            return Changed(node);
        }

        Reduction JSCreateLowering::ReduceJSCreateBoundFunction(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCreateBoundFunction, node->opcode());
            CreateBoundFunctionParameters const& p = CreateBoundFunctionParametersOf(node->op());
            int const arity = static_cast<int>(p.arity());
            MapRef const map(broker(), p.map());
            Node* bound_target_function = NodeProperties::GetValueInput(node, 0);
            Node* bound_this = NodeProperties::GetValueInput(node, 1);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Create the [[BoundArguments]] for the result.
            Node* bound_arguments = jsgraph()->EmptyFixedArrayConstant();
            if (arity > 0) {
                AllocationBuilder a(jsgraph(), effect, control);
                a.AllocateArray(arity, factory()->fixed_array_map());
                for (int i = 0; i < arity; ++i) {
                    a.Store(AccessBuilder::ForFixedArraySlot(i),
                        NodeProperties::GetValueInput(node, 2 + i));
                }
                bound_arguments = effect = a.Finish();
            }

            // Create the JSBoundFunction result.
            AllocationBuilder a(jsgraph(), effect, control);
            a.Allocate(JSBoundFunction::kSize, AllocationType::kYoung,
                Type::BoundFunction());
            a.Store(AccessBuilder::ForMap(), map);
            a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(),
                jsgraph()->EmptyFixedArrayConstant());
            a.Store(AccessBuilder::ForJSObjectElements(),
                jsgraph()->EmptyFixedArrayConstant());
            a.Store(AccessBuilder::ForJSBoundFunctionBoundTargetFunction(),
                bound_target_function);
            a.Store(AccessBuilder::ForJSBoundFunctionBoundThis(), bound_this);
            a.Store(AccessBuilder::ForJSBoundFunctionBoundArguments(), bound_arguments);
            RelaxControls(node);
            a.FinishAndChange(node);
            return Changed(node);
        }

        Reduction JSCreateLowering::ReduceJSCreateClosure(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCreateClosure, node->opcode());
            CreateClosureParameters const& p = CreateClosureParametersOf(node->op());
            SharedFunctionInfoRef shared(broker(), p.shared_info());
            HeapObjectRef feedback_cell(broker(), p.feedback_cell());
            HeapObjectRef code(broker(), p.code());
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* context = NodeProperties::GetContextInput(node);

            // Use inline allocation of closures only for instantiation sites that have
            // seen more than one instantiation, this simplifies the generated code and
            // also serves as a heuristic of which allocation sites benefit from it.
            if (!feedback_cell.map().equals(
                    MapRef(broker(), factory()->many_closures_cell_map()))) {
                return NoChange();
            }

            MapRef function_map = native_context().GetFunctionMapFromIndex(shared.function_map_index());
            DCHECK(!function_map.IsInobjectSlackTrackingInProgress());
            DCHECK(!function_map.is_dictionary_map());

            // TODO(turbofan): We should use the pretenure flag from {p} here,
            // but currently the heuristic in the parser works against us, as
            // it marks closures like
            //
            //   args[l] = function(...) { ... }
            //
            // for old-space allocation, which doesn't always make sense. For
            // example in case of the bluebird-parallel benchmark, where this
            // is a core part of the *promisify* logic (see crbug.com/810132).
            AllocationType allocation = AllocationType::kYoung;

            // Emit code to allocate the JSFunction instance.
            STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kTaggedSize);
            AllocationBuilder a(jsgraph(), effect, control);
            a.Allocate(function_map.instance_size(), allocation, Type::Function());
            a.Store(AccessBuilder::ForMap(), function_map);
            a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(),
                jsgraph()->EmptyFixedArrayConstant());
            a.Store(AccessBuilder::ForJSObjectElements(),
                jsgraph()->EmptyFixedArrayConstant());
            a.Store(AccessBuilder::ForJSFunctionSharedFunctionInfo(), shared);
            a.Store(AccessBuilder::ForJSFunctionContext(), context);
            a.Store(AccessBuilder::ForJSFunctionFeedbackCell(), feedback_cell);
            a.Store(AccessBuilder::ForJSFunctionCode(), code);
            STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kTaggedSize);
            if (function_map.has_prototype_slot()) {
                a.Store(AccessBuilder::ForJSFunctionPrototypeOrInitialMap(),
                    jsgraph()->TheHoleConstant());
                STATIC_ASSERT(JSFunction::kSizeWithPrototype == 8 * kTaggedSize);
            }
            for (int i = 0; i < function_map.GetInObjectProperties(); i++) {
                a.Store(AccessBuilder::ForJSObjectInObjectProperty(function_map, i),
                    jsgraph()->UndefinedConstant());
            }
            RelaxControls(node);
            a.FinishAndChange(node);
            return Changed(node);
        }

        Reduction JSCreateLowering::ReduceJSCreateIterResultObject(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCreateIterResultObject, node->opcode());
            Node* value = NodeProperties::GetValueInput(node, 0);
            Node* done = NodeProperties::GetValueInput(node, 1);
            Node* effect = NodeProperties::GetEffectInput(node);

            Node* iterator_result_map = jsgraph()->Constant(native_context().iterator_result_map());

            // Emit code to allocate the JSIteratorResult instance.
            AllocationBuilder a(jsgraph(), effect, graph()->start());
            a.Allocate(JSIteratorResult::kSize);
            a.Store(AccessBuilder::ForMap(), iterator_result_map);
            a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(),
                jsgraph()->EmptyFixedArrayConstant());
            a.Store(AccessBuilder::ForJSObjectElements(),
                jsgraph()->EmptyFixedArrayConstant());
            a.Store(AccessBuilder::ForJSIteratorResultValue(), value);
            a.Store(AccessBuilder::ForJSIteratorResultDone(), done);
            STATIC_ASSERT(JSIteratorResult::kSize == 5 * kTaggedSize);
            a.FinishAndChange(node);
            return Changed(node);
        }

        Reduction JSCreateLowering::ReduceJSCreateStringIterator(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCreateStringIterator, node->opcode());
            Node* string = NodeProperties::GetValueInput(node, 0);
            Node* effect = NodeProperties::GetEffectInput(node);

            Node* map = jsgraph()->Constant(native_context().initial_string_iterator_map());
            // Allocate new iterator and attach the iterator to this string.
            AllocationBuilder a(jsgraph(), effect, graph()->start());
            a.Allocate(JSStringIterator::kSize, AllocationType::kYoung,
                Type::OtherObject());
            a.Store(AccessBuilder::ForMap(), map);
            a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(),
                jsgraph()->EmptyFixedArrayConstant());
            a.Store(AccessBuilder::ForJSObjectElements(),
                jsgraph()->EmptyFixedArrayConstant());
            a.Store(AccessBuilder::ForJSStringIteratorString(), string);
            a.Store(AccessBuilder::ForJSStringIteratorIndex(), jsgraph()->SmiConstant(0));
            STATIC_ASSERT(JSIteratorResult::kSize == 5 * kTaggedSize);
            a.FinishAndChange(node);
            return Changed(node);
        }

        Reduction JSCreateLowering::ReduceJSCreateKeyValueArray(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCreateKeyValueArray, node->opcode());
            Node* key = NodeProperties::GetValueInput(node, 0);
            Node* value = NodeProperties::GetValueInput(node, 1);
            Node* effect = NodeProperties::GetEffectInput(node);

            Node* array_map = jsgraph()->Constant(native_context().js_array_packed_elements_map());
            Node* properties = jsgraph()->EmptyFixedArrayConstant();
            Node* length = jsgraph()->Constant(2);

            AllocationBuilder aa(jsgraph(), effect, graph()->start());
            aa.AllocateArray(2, factory()->fixed_array_map());
            aa.Store(AccessBuilder::ForFixedArrayElement(PACKED_ELEMENTS),
                jsgraph()->ZeroConstant(), key);
            aa.Store(AccessBuilder::ForFixedArrayElement(PACKED_ELEMENTS),
                jsgraph()->OneConstant(), value);
            Node* elements = aa.Finish();

            AllocationBuilder a(jsgraph(), elements, graph()->start());
            a.Allocate(JSArray::kSize);
            a.Store(AccessBuilder::ForMap(), array_map);
            a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);
            a.Store(AccessBuilder::ForJSObjectElements(), elements);
            a.Store(AccessBuilder::ForJSArrayLength(PACKED_ELEMENTS), length);
            STATIC_ASSERT(JSArray::kSize == 4 * kTaggedSize);
            a.FinishAndChange(node);
            return Changed(node);
        }

        Reduction JSCreateLowering::ReduceJSCreatePromise(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCreatePromise, node->opcode());
            Node* effect = NodeProperties::GetEffectInput(node);

            MapRef promise_map = native_context().promise_function().initial_map();

            AllocationBuilder a(jsgraph(), effect, graph()->start());
            a.Allocate(promise_map.instance_size());
            a.Store(AccessBuilder::ForMap(), promise_map);
            a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(),
                jsgraph()->EmptyFixedArrayConstant());
            a.Store(AccessBuilder::ForJSObjectElements(),
                jsgraph()->EmptyFixedArrayConstant());
            a.Store(AccessBuilder::ForJSObjectOffset(JSPromise::kReactionsOrResultOffset),
                jsgraph()->ZeroConstant());
            STATIC_ASSERT(v8::Promise::kPending == 0);
            a.Store(AccessBuilder::ForJSObjectOffset(JSPromise::kFlagsOffset),
                jsgraph()->ZeroConstant());
            STATIC_ASSERT(JSPromise::kSize == 5 * kTaggedSize);
            for (int offset = JSPromise::kSize;
                 offset < JSPromise::kSizeWithEmbedderFields; offset += kTaggedSize) {
                a.Store(AccessBuilder::ForJSObjectOffset(offset),
                    jsgraph()->ZeroConstant());
            }
            a.FinishAndChange(node);
            return Changed(node);
        }

        Reduction JSCreateLowering::ReduceJSCreateLiteralArrayOrObject(Node* node)
        {
            DCHECK(node->opcode() == IrOpcode::kJSCreateLiteralArray || node->opcode() == IrOpcode::kJSCreateLiteralObject);
            CreateLiteralParameters const& p = CreateLiteralParametersOf(node->op());
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            FeedbackVectorRef feedback_vector(broker(), p.feedback().vector());
            ObjectRef feedback = feedback_vector.get(p.feedback().slot());
            if (feedback.IsAllocationSite()) {
                AllocationSiteRef site = feedback.AsAllocationSite();
                if (site.IsFastLiteral()) {
                    AllocationType allocation = AllocationType::kYoung;
                    if (FLAG_allocation_site_pretenuring) {
                        allocation = dependencies()->DependOnPretenureMode(site);
                    }
                    dependencies()->DependOnElementsKinds(site);
                    JSObjectRef boilerplate = site.boilerplate().value();
                    Node* value = effect = AllocateFastLiteral(effect, control, boilerplate, allocation);
                    ReplaceWithValue(node, value, effect, control);
                    return Replace(value);
                }
            }
            return NoChange();
        }

        Reduction JSCreateLowering::ReduceJSCreateEmptyLiteralArray(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCreateEmptyLiteralArray, node->opcode());
            FeedbackParameter const& p = FeedbackParameterOf(node->op());
            FeedbackVectorRef fv(broker(), p.feedback().vector());
            ObjectRef feedback = fv.get(p.feedback().slot());
            if (feedback.IsAllocationSite()) {
                AllocationSiteRef site = feedback.AsAllocationSite();
                DCHECK(!site.PointsToLiteral());
                MapRef initial_map = native_context().GetInitialJSArrayMap(site.GetElementsKind());
                AllocationType const allocation = dependencies()->DependOnPretenureMode(site);
                dependencies()->DependOnElementsKind(site);
                Node* length = jsgraph()->ZeroConstant();
                DCHECK(!initial_map.IsInobjectSlackTrackingInProgress());
                SlackTrackingPrediction slack_tracking_prediction(
                    initial_map, initial_map.instance_size());
                return ReduceNewArray(node, length, 0, initial_map,
                    initial_map.elements_kind(), allocation,
                    slack_tracking_prediction);
            }
            return NoChange();
        }

        Reduction JSCreateLowering::ReduceJSCreateEmptyLiteralObject(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCreateEmptyLiteralObject, node->opcode());
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Retrieve the initial map for the object.
            MapRef map = native_context().object_function().initial_map();
            DCHECK(!map.is_dictionary_map());
            DCHECK(!map.IsInobjectSlackTrackingInProgress());
            Node* js_object_map = jsgraph()->Constant(map);

            // Setup elements and properties.
            Node* elements = jsgraph()->EmptyFixedArrayConstant();
            Node* properties = jsgraph()->EmptyFixedArrayConstant();

            // Perform the allocation of the actual JSArray object.
            AllocationBuilder a(jsgraph(), effect, control);
            a.Allocate(map.instance_size());
            a.Store(AccessBuilder::ForMap(), js_object_map);
            a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);
            a.Store(AccessBuilder::ForJSObjectElements(), elements);
            for (int i = 0; i < map.GetInObjectProperties(); i++) {
                a.Store(AccessBuilder::ForJSObjectInObjectProperty(map, i),
                    jsgraph()->UndefinedConstant());
            }

            RelaxControls(node);
            a.FinishAndChange(node);
            return Changed(node);
        }

        Reduction JSCreateLowering::ReduceJSCreateLiteralRegExp(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCreateLiteralRegExp, node->opcode());
            CreateLiteralParameters const& p = CreateLiteralParametersOf(node->op());
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            FeedbackVectorRef feedback_vector(broker(), p.feedback().vector());
            ObjectRef feedback = feedback_vector.get(p.feedback().slot());
            if (feedback.IsJSRegExp()) {
                JSRegExpRef boilerplate = feedback.AsJSRegExp();
                Node* value = effect = AllocateLiteralRegExp(effect, control, boilerplate);
                ReplaceWithValue(node, value, effect, control);
                return Replace(value);
            }
            return NoChange();
        }

        Reduction JSCreateLowering::ReduceJSCreateFunctionContext(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCreateFunctionContext, node->opcode());
            const CreateFunctionContextParameters& parameters = CreateFunctionContextParametersOf(node->op());
            ScopeInfoRef scope_info(broker(), parameters.scope_info());
            int slot_count = parameters.slot_count();
            ScopeType scope_type = parameters.scope_type();

            // Use inline allocation for function contexts up to a size limit.
            if (slot_count < kFunctionContextAllocationLimit) {
                // JSCreateFunctionContext[slot_count < limit]](fun)
                Node* effect = NodeProperties::GetEffectInput(node);
                Node* control = NodeProperties::GetControlInput(node);
                Node* context = NodeProperties::GetContextInput(node);
                Node* extension = jsgraph()->TheHoleConstant();
                AllocationBuilder a(jsgraph(), effect, control);
                STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == 4); // Ensure fully covered.
                int context_length = slot_count + Context::MIN_CONTEXT_SLOTS;
                Handle<Map> map;
                switch (scope_type) {
                case EVAL_SCOPE:
                    map = factory()->eval_context_map();
                    break;
                case FUNCTION_SCOPE:
                    map = factory()->function_context_map();
                    break;
                default:
                    UNREACHABLE();
                }
                a.AllocateContext(context_length, map);
                a.Store(AccessBuilder::ForContextSlot(Context::SCOPE_INFO_INDEX),
                    scope_info);
                a.Store(AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX), context);
                a.Store(AccessBuilder::ForContextSlot(Context::EXTENSION_INDEX), extension);
                a.Store(AccessBuilder::ForContextSlot(Context::NATIVE_CONTEXT_INDEX),
                    jsgraph()->Constant(native_context()));
                for (int i = Context::MIN_CONTEXT_SLOTS; i < context_length; ++i) {
                    a.Store(AccessBuilder::ForContextSlot(i), jsgraph()->UndefinedConstant());
                }
                RelaxControls(node);
                a.FinishAndChange(node);
                return Changed(node);
            }

            return NoChange();
        }

        Reduction JSCreateLowering::ReduceJSCreateWithContext(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCreateWithContext, node->opcode());
            ScopeInfoRef scope_info(broker(), ScopeInfoOf(node->op()));
            Node* extension = NodeProperties::GetValueInput(node, 0);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* context = NodeProperties::GetContextInput(node);

            AllocationBuilder a(jsgraph(), effect, control);
            STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == 4); // Ensure fully covered.
            a.AllocateContext(Context::MIN_CONTEXT_SLOTS, factory()->with_context_map());
            a.Store(AccessBuilder::ForContextSlot(Context::SCOPE_INFO_INDEX), scope_info);
            a.Store(AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX), context);
            a.Store(AccessBuilder::ForContextSlot(Context::EXTENSION_INDEX), extension);
            a.Store(AccessBuilder::ForContextSlot(Context::NATIVE_CONTEXT_INDEX),
                jsgraph()->Constant(native_context()));
            RelaxControls(node);
            a.FinishAndChange(node);
            return Changed(node);
        }

        Reduction JSCreateLowering::ReduceJSCreateCatchContext(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCreateCatchContext, node->opcode());
            ScopeInfoRef scope_info(broker(), ScopeInfoOf(node->op()));
            Node* exception = NodeProperties::GetValueInput(node, 0);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* context = NodeProperties::GetContextInput(node);
            Node* extension = jsgraph()->TheHoleConstant();

            AllocationBuilder a(jsgraph(), effect, control);
            STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == 4); // Ensure fully covered.
            a.AllocateContext(Context::MIN_CONTEXT_SLOTS + 1,
                factory()->catch_context_map());
            a.Store(AccessBuilder::ForContextSlot(Context::SCOPE_INFO_INDEX), scope_info);
            a.Store(AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX), context);
            a.Store(AccessBuilder::ForContextSlot(Context::EXTENSION_INDEX), extension);
            a.Store(AccessBuilder::ForContextSlot(Context::NATIVE_CONTEXT_INDEX),
                jsgraph()->Constant(native_context()));
            a.Store(AccessBuilder::ForContextSlot(Context::THROWN_OBJECT_INDEX),
                exception);
            RelaxControls(node);
            a.FinishAndChange(node);
            return Changed(node);
        }

        Reduction JSCreateLowering::ReduceJSCreateBlockContext(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCreateBlockContext, node->opcode());
            ScopeInfoRef scope_info(broker(), ScopeInfoOf(node->op()));
            int const context_length = scope_info.ContextLength();

            // Use inline allocation for block contexts up to a size limit.
            if (context_length < kBlockContextAllocationLimit) {
                // JSCreateBlockContext[scope[length < limit]](fun)
                Node* effect = NodeProperties::GetEffectInput(node);
                Node* control = NodeProperties::GetControlInput(node);
                Node* context = NodeProperties::GetContextInput(node);
                Node* extension = jsgraph()->TheHoleConstant();

                AllocationBuilder a(jsgraph(), effect, control);
                STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == 4); // Ensure fully covered.
                a.AllocateContext(context_length, factory()->block_context_map());
                a.Store(AccessBuilder::ForContextSlot(Context::SCOPE_INFO_INDEX),
                    scope_info);
                a.Store(AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX), context);
                a.Store(AccessBuilder::ForContextSlot(Context::EXTENSION_INDEX), extension);
                a.Store(AccessBuilder::ForContextSlot(Context::NATIVE_CONTEXT_INDEX),
                    jsgraph()->Constant(native_context()));
                for (int i = Context::MIN_CONTEXT_SLOTS; i < context_length; ++i) {
                    a.Store(AccessBuilder::ForContextSlot(i), jsgraph()->UndefinedConstant());
                }
                RelaxControls(node);
                a.FinishAndChange(node);
                return Changed(node);
            }

            return NoChange();
        }

        namespace {
            base::Optional<MapRef> GetObjectCreateMap(JSHeapBroker* broker,
                HeapObjectRef prototype)
            {
                MapRef standard_map = broker->native_context().object_function().initial_map();
                if (prototype.equals(standard_map.prototype())) {
                    return standard_map;
                }
                if (prototype.map().oddball_type() == OddballType::kNull) {
                    return broker->native_context().slow_object_with_null_prototype_map();
                }
                if (prototype.IsJSObject()) {
                    return prototype.AsJSObject().GetObjectCreateMap();
                }
                return base::Optional<MapRef>();
            }
        } // namespace

        Reduction JSCreateLowering::ReduceJSCreateObject(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCreateObject, node->opcode());
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* prototype = NodeProperties::GetValueInput(node, 0);
            Type prototype_type = NodeProperties::GetType(prototype);
            if (!prototype_type.IsHeapConstant())
                return NoChange();

            HeapObjectRef prototype_const = prototype_type.AsHeapConstant()->Ref();
            auto maybe_instance_map = GetObjectCreateMap(broker(), prototype_const);
            if (!maybe_instance_map)
                return NoChange();
            MapRef instance_map = maybe_instance_map.value();

            Node* properties = jsgraph()->EmptyFixedArrayConstant();
            if (instance_map.is_dictionary_map()) {
                DCHECK_EQ(prototype_const.map().oddball_type(), OddballType::kNull);
                // Allocate an empty NameDictionary as backing store for the properties.
                MapRef map(broker(), factory()->name_dictionary_map());
                int capacity = NameDictionary::ComputeCapacity(NameDictionary::kInitialCapacity);
                DCHECK(base::bits::IsPowerOfTwo(capacity));
                int length = NameDictionary::EntryToIndex(capacity);
                int size = NameDictionary::SizeFor(length);

                AllocationBuilder a(jsgraph(), effect, control);
                a.Allocate(size, AllocationType::kYoung, Type::Any());
                a.Store(AccessBuilder::ForMap(), map);
                // Initialize FixedArray fields.
                a.Store(AccessBuilder::ForFixedArrayLength(),
                    jsgraph()->SmiConstant(length));
                // Initialize HashTable fields.
                a.Store(AccessBuilder::ForHashTableBaseNumberOfElements(),
                    jsgraph()->SmiConstant(0));
                a.Store(AccessBuilder::ForHashTableBaseNumberOfDeletedElement(),
                    jsgraph()->SmiConstant(0));
                a.Store(AccessBuilder::ForHashTableBaseCapacity(),
                    jsgraph()->SmiConstant(capacity));
                // Initialize Dictionary fields.
                a.Store(AccessBuilder::ForDictionaryNextEnumerationIndex(),
                    jsgraph()->SmiConstant(PropertyDetails::kInitialIndex));
                a.Store(AccessBuilder::ForDictionaryObjectHashIndex(),
                    jsgraph()->SmiConstant(PropertyArray::kNoHashSentinel));
                // Initialize the Properties fields.
                Node* undefined = jsgraph()->UndefinedConstant();
                STATIC_ASSERT(NameDictionary::kElementsStartIndex == NameDictionary::kObjectHashIndex + 1);
                for (int index = NameDictionary::kElementsStartIndex; index < length;
                     index++) {
                    a.Store(AccessBuilder::ForFixedArraySlot(index, kNoWriteBarrier),
                        undefined);
                }
                properties = effect = a.Finish();
            }

            int const instance_size = instance_map.instance_size();
            if (instance_size > kMaxRegularHeapObjectSize)
                return NoChange();
            CHECK(!instance_map.IsInobjectSlackTrackingInProgress());

            // Emit code to allocate the JSObject instance for the given
            // {instance_map}.
            AllocationBuilder a(jsgraph(), effect, control);
            a.Allocate(instance_size, AllocationType::kYoung, Type::Any());
            a.Store(AccessBuilder::ForMap(), instance_map);
            a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);
            a.Store(AccessBuilder::ForJSObjectElements(),
                jsgraph()->EmptyFixedArrayConstant());
            // Initialize Object fields.
            Node* undefined = jsgraph()->UndefinedConstant();
            for (int offset = JSObject::kHeaderSize; offset < instance_size;
                 offset += kTaggedSize) {
                a.Store(AccessBuilder::ForJSObjectOffset(offset, kNoWriteBarrier),
                    undefined);
            }
            Node* value = effect = a.Finish();

            ReplaceWithValue(node, value, effect, control);
            return Replace(value);
        }

        // Helper that allocates a FixedArray holding argument values recorded in the
        // given {frame_state}. Serves as backing store for JSCreateArguments nodes.
        Node* JSCreateLowering::AllocateArguments(Node* effect, Node* control,
            Node* frame_state)
        {
            FrameStateInfo state_info = FrameStateInfoOf(frame_state->op());
            int argument_count = state_info.parameter_count() - 1; // Minus receiver.
            if (argument_count == 0)
                return jsgraph()->EmptyFixedArrayConstant();

            // Prepare an iterator over argument values recorded in the frame state.
            Node* const parameters = frame_state->InputAt(kFrameStateParametersInput);
            StateValuesAccess parameters_access(parameters);
            auto parameters_it = ++parameters_access.begin();

            // Actually allocate the backing store.
            AllocationBuilder a(jsgraph(), effect, control);
            a.AllocateArray(argument_count, factory()->fixed_array_map());
            for (int i = 0; i < argument_count; ++i, ++parameters_it) {
                DCHECK_NOT_NULL((*parameters_it).node);
                a.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(i),
                    (*parameters_it).node);
            }
            return a.Finish();
        }

        // Helper that allocates a FixedArray holding argument values recorded in the
        // given {frame_state}. Serves as backing store for JSCreateArguments nodes.
        Node* JSCreateLowering::AllocateRestArguments(Node* effect, Node* control,
            Node* frame_state,
            int start_index)
        {
            FrameStateInfo state_info = FrameStateInfoOf(frame_state->op());
            int argument_count = state_info.parameter_count() - 1; // Minus receiver.
            int num_elements = std::max(0, argument_count - start_index);
            if (num_elements == 0)
                return jsgraph()->EmptyFixedArrayConstant();

            // Prepare an iterator over argument values recorded in the frame state.
            Node* const parameters = frame_state->InputAt(kFrameStateParametersInput);
            StateValuesAccess parameters_access(parameters);
            auto parameters_it = ++parameters_access.begin();

            // Skip unused arguments.
            for (int i = 0; i < start_index; i++) {
                ++parameters_it;
            }

            // Actually allocate the backing store.
            AllocationBuilder a(jsgraph(), effect, control);
            a.AllocateArray(num_elements, factory()->fixed_array_map());
            for (int i = 0; i < num_elements; ++i, ++parameters_it) {
                DCHECK_NOT_NULL((*parameters_it).node);
                a.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(i),
                    (*parameters_it).node);
            }
            return a.Finish();
        }

        // Helper that allocates a FixedArray serving as a parameter map for values
        // recorded in the given {frame_state}. Some elements map to slots within the
        // given {context}. Serves as backing store for JSCreateArguments nodes.
        Node* JSCreateLowering::AllocateAliasedArguments(
            Node* effect, Node* control, Node* frame_state, Node* context,
            const SharedFunctionInfoRef& shared, bool* has_aliased_arguments)
        {
            FrameStateInfo state_info = FrameStateInfoOf(frame_state->op());
            int argument_count = state_info.parameter_count() - 1; // Minus receiver.
            if (argument_count == 0)
                return jsgraph()->EmptyFixedArrayConstant();

            // If there is no aliasing, the arguments object elements are not special in
            // any way, we can just return an unmapped backing store instead.
            int parameter_count = shared.internal_formal_parameter_count();
            if (parameter_count == 0) {
                return AllocateArguments(effect, control, frame_state);
            }

            // Calculate number of argument values being aliased/mapped.
            int mapped_count = Min(argument_count, parameter_count);
            *has_aliased_arguments = true;

            // Prepare an iterator over argument values recorded in the frame state.
            Node* const parameters = frame_state->InputAt(kFrameStateParametersInput);
            StateValuesAccess parameters_access(parameters);
            auto parameters_it = ++parameters_access.begin();

            // The unmapped argument values recorded in the frame state are stored yet
            // another indirection away and then linked into the parameter map below,
            // whereas mapped argument values are replaced with a hole instead.
            AllocationBuilder aa(jsgraph(), effect, control);
            aa.AllocateArray(argument_count, factory()->fixed_array_map());
            for (int i = 0; i < mapped_count; ++i, ++parameters_it) {
                aa.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(i),
                    jsgraph()->TheHoleConstant());
            }
            for (int i = mapped_count; i < argument_count; ++i, ++parameters_it) {
                DCHECK_NOT_NULL((*parameters_it).node);
                aa.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(i),
                    (*parameters_it).node);
            }
            Node* arguments = aa.Finish();

            // Actually allocate the backing store.
            AllocationBuilder a(jsgraph(), arguments, control);
            a.AllocateArray(mapped_count + 2, factory()->sloppy_arguments_elements_map());
            a.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(0),
                context);
            a.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(1),
                arguments);
            for (int i = 0; i < mapped_count; ++i) {
                int idx = Context::MIN_CONTEXT_SLOTS + parameter_count - 1 - i;
                a.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(i + 2),
                    jsgraph()->Constant(idx));
            }
            return a.Finish();
        }

        // Helper that allocates a FixedArray serving as a parameter map for values
        // unknown at compile-time, the true {arguments_length} and {arguments_frame}
        // values can only be determined dynamically at run-time and are provided.
        // Serves as backing store for JSCreateArguments nodes.
        Node* JSCreateLowering::AllocateAliasedArguments(
            Node* effect, Node* control, Node* context, Node* arguments_frame,
            Node* arguments_length, const SharedFunctionInfoRef& shared,
            bool* has_aliased_arguments)
        {
            // If there is no aliasing, the arguments object elements are not
            // special in any way, we can just return an unmapped backing store.
            int parameter_count = shared.internal_formal_parameter_count();
            if (parameter_count == 0) {
                return graph()->NewNode(simplified()->NewArgumentsElements(0),
                    arguments_frame, arguments_length, effect);
            }

            // From here on we are going to allocate a mapped (aka. aliased) elements
            // backing store. We do not statically know how many arguments exist, but
            // dynamically selecting the hole for some of the "mapped" elements allows
            // using a static shape for the parameter map.
            int mapped_count = parameter_count;
            *has_aliased_arguments = true;

            // The unmapped argument values are stored yet another indirection away and
            // then linked into the parameter map below, whereas mapped argument values
            // (i.e. the first {mapped_count} elements) are replaced with a hole instead.
            Node* arguments = graph()->NewNode(simplified()->NewArgumentsElements(mapped_count),
                arguments_frame, arguments_length, effect);

            // Actually allocate the backing store.
            AllocationBuilder a(jsgraph(), arguments, control);
            a.AllocateArray(mapped_count + 2, factory()->sloppy_arguments_elements_map());
            a.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(0),
                context);
            a.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(1),
                arguments);
            for (int i = 0; i < mapped_count; ++i) {
                int idx = Context::MIN_CONTEXT_SLOTS + parameter_count - 1 - i;
                Node* value = graph()->NewNode(
                    common()->Select(MachineRepresentation::kTagged),
                    graph()->NewNode(simplified()->NumberLessThan(), jsgraph()->Constant(i),
                        arguments_length),
                    jsgraph()->Constant(idx), jsgraph()->TheHoleConstant());
                a.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(i + 2),
                    value);
            }
            return a.Finish();
        }

        Node* JSCreateLowering::AllocateElements(Node* effect, Node* control,
            ElementsKind elements_kind,
            int capacity,
            AllocationType allocation)
        {
            DCHECK_LE(1, capacity);
            DCHECK_LE(capacity, JSArray::kInitialMaxFastElementArray);

            Handle<Map> elements_map = IsDoubleElementsKind(elements_kind)
                ? factory()->fixed_double_array_map()
                : factory()->fixed_array_map();
            ElementAccess access = IsDoubleElementsKind(elements_kind)
                ? AccessBuilder::ForFixedDoubleArrayElement()
                : AccessBuilder::ForFixedArrayElement();
            Node* value = jsgraph()->TheHoleConstant();

            // Actually allocate the backing store.
            AllocationBuilder a(jsgraph(), effect, control);
            a.AllocateArray(capacity, elements_map, allocation);
            for (int i = 0; i < capacity; ++i) {
                Node* index = jsgraph()->Constant(i);
                a.Store(access, index, value);
            }
            return a.Finish();
        }

        Node* JSCreateLowering::AllocateElements(Node* effect, Node* control,
            ElementsKind elements_kind,
            std::vector<Node*> const& values,
            AllocationType allocation)
        {
            int const capacity = static_cast<int>(values.size());
            DCHECK_LE(1, capacity);
            DCHECK_LE(capacity, JSArray::kInitialMaxFastElementArray);

            Handle<Map> elements_map = IsDoubleElementsKind(elements_kind)
                ? factory()->fixed_double_array_map()
                : factory()->fixed_array_map();
            ElementAccess access = IsDoubleElementsKind(elements_kind)
                ? AccessBuilder::ForFixedDoubleArrayElement()
                : AccessBuilder::ForFixedArrayElement();

            // Actually allocate the backing store.
            AllocationBuilder a(jsgraph(), effect, control);
            a.AllocateArray(capacity, elements_map, allocation);
            for (int i = 0; i < capacity; ++i) {
                Node* index = jsgraph()->Constant(i);
                a.Store(access, index, values[i]);
            }
            return a.Finish();
        }

        Node* JSCreateLowering::AllocateFastLiteral(Node* effect, Node* control,
            JSObjectRef boilerplate,
            AllocationType allocation)
        {
            // Setup the properties backing store.
            Node* properties = jsgraph()->EmptyFixedArrayConstant();

            // Compute the in-object properties to store first (might have effects).
            MapRef boilerplate_map = boilerplate.map();
            ZoneVector<std::pair<FieldAccess, Node*>> inobject_fields(zone());
            inobject_fields.reserve(boilerplate_map.GetInObjectProperties());
            int const boilerplate_nof = boilerplate_map.NumberOfOwnDescriptors();
            for (int i = 0; i < boilerplate_nof; ++i) {
                PropertyDetails const property_details = boilerplate_map.GetPropertyDetails(i);
                if (property_details.location() != kField)
                    continue;
                DCHECK_EQ(kData, property_details.kind());
                NameRef property_name = boilerplate_map.GetPropertyKey(i);
                FieldIndex index = boilerplate_map.GetFieldIndexFor(i);
                FieldAccess access = {
                    kTaggedBase, index.offset(), property_name.object(),
                    MaybeHandle<Map>(), Type::Any(), MachineType::AnyTagged(),
                    kFullWriteBarrier
                };
                Node* value;
                if (boilerplate_map.IsUnboxedDoubleField(i)) {
                    access.machine_type = MachineType::Float64();
                    access.type = Type::Number();
                    value = jsgraph()->Constant(boilerplate.RawFastDoublePropertyAt(index));
                } else {
                    ObjectRef boilerplate_value = boilerplate.RawFastPropertyAt(index);
                    if (boilerplate_value.IsJSObject()) {
                        JSObjectRef boilerplate_object = boilerplate_value.AsJSObject();
                        value = effect = AllocateFastLiteral(effect, control,
                            boilerplate_object, allocation);
                    } else if (property_details.representation().IsDouble()) {
                        double number = boilerplate_value.AsMutableHeapNumber().value();
                        // Allocate a mutable HeapNumber box and store the value into it.
                        AllocationBuilder builder(jsgraph(), effect, control);
                        builder.Allocate(HeapNumber::kSize, allocation);
                        builder.Store(AccessBuilder::ForMap(),
                            factory()->mutable_heap_number_map());
                        builder.Store(AccessBuilder::ForHeapNumberValue(),
                            jsgraph()->Constant(number));
                        value = effect = builder.Finish();
                    } else if (property_details.representation().IsSmi()) {
                        // Ensure that value is stored as smi.
                        bool is_uninitialized = boilerplate_value.IsHeapObject() && boilerplate_value.AsHeapObject().map().oddball_type() == OddballType::kUninitialized;
                        value = is_uninitialized
                            ? jsgraph()->ZeroConstant()
                            : jsgraph()->Constant(boilerplate_value.AsSmi());
                    } else {
                        value = jsgraph()->Constant(boilerplate_value);
                    }
                }
                inobject_fields.push_back(std::make_pair(access, value));
            }

            // Fill slack at the end of the boilerplate object with filler maps.
            int const boilerplate_length = boilerplate_map.GetInObjectProperties();
            for (int index = static_cast<int>(inobject_fields.size());
                 index < boilerplate_length; ++index) {
                FieldAccess access = AccessBuilder::ForJSObjectInObjectProperty(boilerplate_map, index);
                Node* value = jsgraph()->HeapConstant(factory()->one_pointer_filler_map());
                inobject_fields.push_back(std::make_pair(access, value));
            }

            // Setup the elements backing store.
            Node* elements = AllocateFastLiteralElements(effect, control, boilerplate, allocation);
            if (elements->op()->EffectOutputCount() > 0)
                effect = elements;

            // Actually allocate and initialize the object.
            AllocationBuilder builder(jsgraph(), effect, control);
            builder.Allocate(boilerplate_map.instance_size(), allocation,
                Type::For(boilerplate_map));
            builder.Store(AccessBuilder::ForMap(), boilerplate_map);
            builder.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);
            builder.Store(AccessBuilder::ForJSObjectElements(), elements);
            if (boilerplate.IsJSArray()) {
                JSArrayRef boilerplate_array = boilerplate.AsJSArray();
                builder.Store(
                    AccessBuilder::ForJSArrayLength(boilerplate_array.GetElementsKind()),
                    boilerplate_array.length());
            }
            for (auto const& inobject_field : inobject_fields) {
                builder.Store(inobject_field.first, inobject_field.second);
            }
            return builder.Finish();
        }

        Node* JSCreateLowering::AllocateFastLiteralElements(Node* effect, Node* control,
            JSObjectRef boilerplate,
            AllocationType allocation)
        {
            FixedArrayBaseRef boilerplate_elements = boilerplate.elements();

            // Empty or copy-on-write elements just store a constant.
            int const elements_length = boilerplate_elements.length();
            MapRef elements_map = boilerplate_elements.map();
            if (boilerplate_elements.length() == 0 || elements_map.IsFixedCowArrayMap()) {
                if (allocation == AllocationType::kOld) {
                    boilerplate.EnsureElementsTenured();
                    boilerplate_elements = boilerplate.elements();
                }
                return jsgraph()->HeapConstant(boilerplate_elements.object());
            }

            // Compute the elements to store first (might have effects).
            ZoneVector<Node*> elements_values(elements_length, zone());
            if (elements_map.instance_type() == FIXED_DOUBLE_ARRAY_TYPE) {
                FixedDoubleArrayRef elements = boilerplate_elements.AsFixedDoubleArray();
                for (int i = 0; i < elements_length; ++i) {
                    if (elements.is_the_hole(i)) {
                        elements_values[i] = jsgraph()->TheHoleConstant();
                    } else {
                        elements_values[i] = jsgraph()->Constant(elements.get_scalar(i));
                    }
                }
            } else {
                FixedArrayRef elements = boilerplate_elements.AsFixedArray();
                for (int i = 0; i < elements_length; ++i) {
                    ObjectRef element_value = elements.get(i);
                    if (element_value.IsJSObject()) {
                        elements_values[i] = effect = AllocateFastLiteral(
                            effect, control, element_value.AsJSObject(), allocation);
                    } else {
                        elements_values[i] = jsgraph()->Constant(element_value);
                    }
                }
            }

            // Allocate the backing store array and store the elements.
            AllocationBuilder builder(jsgraph(), effect, control);
            builder.AllocateArray(elements_length, elements_map.object(), allocation);
            ElementAccess const access = (elements_map.instance_type() == FIXED_DOUBLE_ARRAY_TYPE)
                ? AccessBuilder::ForFixedDoubleArrayElement()
                : AccessBuilder::ForFixedArrayElement();
            for (int i = 0; i < elements_length; ++i) {
                builder.Store(access, jsgraph()->Constant(i), elements_values[i]);
            }
            return builder.Finish();
        }

        Node* JSCreateLowering::AllocateLiteralRegExp(Node* effect, Node* control,
            JSRegExpRef boilerplate)
        {
            MapRef boilerplate_map = boilerplate.map();

            // Sanity check that JSRegExp object layout hasn't changed.
            STATIC_ASSERT(static_cast<int>(JSRegExp::kDataOffset) == static_cast<int>(JSObject::kHeaderSize));
            STATIC_ASSERT(JSRegExp::kSourceOffset == JSRegExp::kDataOffset + kTaggedSize);
            STATIC_ASSERT(JSRegExp::kFlagsOffset == JSRegExp::kSourceOffset + kTaggedSize);
            STATIC_ASSERT(JSRegExp::kSize == JSRegExp::kFlagsOffset + kTaggedSize);
            STATIC_ASSERT(JSRegExp::kLastIndexOffset == JSRegExp::kSize);
            STATIC_ASSERT(JSRegExp::kInObjectFieldCount == 1); // LastIndex.

            const AllocationType allocation = AllocationType::kYoung;
            const int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kTaggedSize;

            AllocationBuilder builder(jsgraph(), effect, control);
            builder.Allocate(size, allocation, Type::For(boilerplate_map));
            builder.Store(AccessBuilder::ForMap(), boilerplate_map);
            builder.Store(AccessBuilder::ForJSObjectPropertiesOrHash(),
                boilerplate.raw_properties_or_hash());
            builder.Store(AccessBuilder::ForJSObjectElements(), boilerplate.elements());

            builder.Store(AccessBuilder::ForJSRegExpData(), boilerplate.data());
            builder.Store(AccessBuilder::ForJSRegExpSource(), boilerplate.source());
            builder.Store(AccessBuilder::ForJSRegExpFlags(), boilerplate.flags());
            builder.Store(AccessBuilder::ForJSRegExpLastIndex(),
                boilerplate.last_index());

            return builder.Finish();
        }

        Factory* JSCreateLowering::factory() const
        {
            return jsgraph()->isolate()->factory();
        }

        Graph* JSCreateLowering::graph() const { return jsgraph()->graph(); }

        CommonOperatorBuilder* JSCreateLowering::common() const
        {
            return jsgraph()->common();
        }

        SimplifiedOperatorBuilder* JSCreateLowering::simplified() const
        {
            return jsgraph()->simplified();
        }

        NativeContextRef JSCreateLowering::native_context() const
        {
            return broker()->native_context();
        }

    } // namespace compiler
} // namespace internal
} // namespace v8
